package http

import (
	"bytes"
	"errors"
	"gitee.com/simple-set/simple.io/src/process/codec"
	"io"
)

type Body struct {
	size int64
	buff *codec.ByteBuf
}

func (b *Body) Size() int64 {
	return b.size
}

func (b *Body) Write(p []byte) (int, error) {
	buffer := b.buff.WriteBuffer()
	if buffer == nil {
		return -1, errors.New("buffer cannot be written")
	}

	if n, err := buffer.Write(p); err != nil {
		return n, err
	} else {
		_ = buffer.Flush()
		b.size += int64(n)
		return n, nil
	}
}

func (b *Body) WriteString(data string) (int, error) {
	buffer := b.buff.WriteBuffer()
	if buffer == nil {
		return -1, errors.New("buffer cannot be written")
	}

	if n, err := buffer.WriteString(data); err != nil {
		return n, err
	} else {
		_ = buffer.Flush()
		b.size += int64(n)
		return n, nil
	}
}

func (b *Body) Read(p []byte) (n int, err error) {
	buffer := b.buff.ReadBuffer()
	if buffer == nil {
		return 0, errors.New("unable to read buffer")
	} else if b.size <= 0 {
		return 0, io.EOF
	}

	if int64(cap(p)) > b.size {
		// 限制读取长度
		n, err = buffer.Read(p[:b.size])
	} else {
		n, err = buffer.Read(p)
	}

	if err != nil {
		return n, err
	}
	b.size -= int64(n)
	return n, nil
}

func (b *Body) ReadBytes() ([]byte, error) {
	if b.size > 0 {
		buff := make([]byte, b.size)
		if _, err := b.Read(buff); err != nil {
			return nil, err
		}
		return buff, nil
	}
	return nil, errors.New("buffer is empty")
}

func (b *Body) ReadString() (string, error) {
	if readBytes, err := b.ReadBytes(); err == nil {
		return string(readBytes), nil
	} else {
		return "", err
	}
}

func NewBody() *Body {
	buffer := bytes.NewBuffer(make([]byte, 0, 32))
	byteBuf := codec.NewReadWriteByteBuf(buffer, buffer)
	return &Body{size: 0, buff: byteBuf}
}

func NewReadBody(size int64, reader io.Reader) *Body {
	return &Body{size: size, buff: codec.NewReadByteBuf(reader)}
}

func NewWriteBody(writer io.Writer) *Body {
	return &Body{buff: codec.NewWriteByteBuf(writer)}
}

func NewReaderWriteBody(reader io.Reader, writer io.Writer) *Body {
	return &Body{buff: codec.NewReadWriteByteBuf(reader, writer)}
}
